# -*- coding: utf-8 -*-
# @Time    : 2024/6/18 11:19
# @Author  : yujiahao
# @File    : 28_class_extend.py
# @description: 类的继承


# 一 什么是面向对象的继承
r'''



Python

比较官方的说法就是：

继承（英语：inheritance）是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B，就把这个A称为“B的子类别”，而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法，而不需要再次编写相同的代码。在令子类别继承父类别的同时，可以重新定义某些属性，并重写某些方法，即覆盖父类别的原有属性和方法，使其获得与父类别不同的功能。另外，为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言，继承属于静态的，意即在子类别的行为在编译期就已经决定，无法在执行期扩充。

字面意思就是：子承父业，合法继承家产，就是如果你是独生子，而且你也很孝顺，不出意外，你会继承你父母所有家产，他们的所有财产都会由你使用（败家子儿除外）。

【继承的语法】 通过简单地在类定义时将基类名称放在括号内来实现：

        class BaseClass:
            pass

        class DerivedClass(BaseClass):
            pass

【继承的特点】

    1，增加了类的耦合性（耦合性不宜多，宜精）。

    2，减少了重复代码。

    3，使得代码更加规范化，合理化。

下面的例子： Aminal 叫做父类,基类,超类。 Person Cat Dog: 子类，派生类。 继承：可以分单继承，多继承。

        # 普通写法
        class Person:
            def __init__(self,name,sex,age):
                self.name = name
                self.age = age
                self.sex = sex

        class Cat:
            def __init__(self,name,sex,age):
                self.name = name
                self.age = age
                self.sex = sex

        class Dog:
            def __init__(self,name,sex,age):
                self.name = name
                self.age = age
                self.sex = sex

        # 继承的用法：
        class Aniaml(object):
            def __init__(self,name,sex,age):
                    self.name = name
                    self.age = age
                    self.sex = sex


        class Person(Aniaml):
            pass

        class Cat(Aniaml):
            pass

        class Dog(Aniaml):
            pass


【继承的分类】

这里需要补充一下python中类的种类（继承需要）：

    在python2x版本中存在两种类：
        ⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写.
     　 ⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
     python3x版本中只有一种类： python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object（Java中这个是所有类的基类）

Java

 继承：
    1>定义：从现有类创建子类，现有类称为父类，基类(子类以父类为基础进行扩充），超类（在子类中通过super限定来特指父类成员)
       子类要继承父类的所有成员，构造器除外
    2>内在逻辑：父类不够用，要扩充子类一些其他的让他更强大，所以就有了子类
    3>为什么要有继承：多个类中存在相同属性和行为时，将这些内容抽取到单独一个类中，那么多个类无需再定义这些属性和行为，只要继承那个类即可.

    4>几点说明;
        ①java中只支持单层继承，不允许多重继承，但是可以多层继承，因为多重继承时，如果多个直接父类中都有同名方法子类会混乱
        ②子类继承父类私有成员，但是不能直接使用，因为被private修饰成员只能在本类中使用，子类不是本类的成员，所以在子类中不能被直接使用
        ③可以在父类中添加公共的getset方法，可以从父类继承的公共的getset方法间接访问
        ④在内存中，创建的子类对象中会包含父类中所有的属性，所以就可以把子类对象“当成”父类来使用
        ⑤父类是被动的，永远不知道那个子类继承了它
    5> 方法覆盖（Override):子类根据需要重写从父类继承的方法.--最重要的概念，没有之一；
         1>> 覆盖条件：
           	1) 方法签名完全一致, 包括返回值类型, 方法名, 参数列表(类型, 顺序, 个数)
               返回值类型如果是引用类型, 在子类重写方法时, 返回值的类型可以是父类方法返回值类型的子类类型
           	2) 子类重写方法的访问控制修饰符要大于等于父类中的方法的访问控制修饰符
        	3) 父类中和子类中的方法都必须是非static的
           	4) 子类重写方法抛出的受检异常要小于等于父类方法抛出的受检异常.
         2>>方法重载建议都要在重载方法前加 @Override
            >> @Override本质是修饰符，也可以放在public的前面，
            >> 注解：特殊的注释，可以被编译器和VM识别的，// 请求编译器，请帮助我做方法覆盖的条件和检查，如果这个方法的覆盖出问题，请编译出错

    6> super关键字--在Java类中使用super来调用父类中的指定操作：
        >>作用：
            1> super可用于访问父类中定义的属性
            2> super可用于调用父类中定义的成员方法
            3> super可用于在子类构造方法中调用父类的构造器
            4> super.say() 指定的是从父类继承过来的say;super

        >>注意(和this关键字区别）：
                1> 尤其当子父类出现同名成员时，可以用super进行区分
                2> super的追溯不仅限于直接父类
                3> super和this的用法相像，this代表本类对象的引用，super代表父类的内存空间的标识
                4>this表示当前对象整体，super只是一个内存标识用于限定从父类继承的成员

    7> 访问权限修饰符：
         private：本类
         default：本类 本包其他类
         protected： 本类   本包其他类  其他包子类
         public ：全局
    8> 子类构造器：
      >>说明：
        1> 所有子类构造器默认会调用父类的无参构造器，先调用父类的在调用自己的，会默认在子类构造器前加super（）；必须第一行
        2> 如果父类没有无参构造器，子类就会出错，因为子类需要无参构造器
        3>在子类中, 虽然没有写构造器, 但是它的缺省构造器一定要调用父类的无参构造器.
      >>结论：
            1> 子类构造器中的第一行必须是this（...）或者super(...),
            2> this(...)间接调用父类构造器
            3> super（...）直接调用父类构造器
        >> 结论：子类构造中必须先调用父类构造，先父后子天经地义!


Scala

    package com.yujiahao.bigdata.scala.chapter07

    import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor.SetterOnlyReflection

    import scala.io.Codec.fallbackSystemCodec.name

    /**
     *TODO 面向对象 - 继承
     *面向对象的思想：关系（血缘）
      *TODO 面向对象 - 继承--构造方法
     *构建子类对象时，父类对象会先构建出来，所以先父后子，父类构造方法先执行，并先初始化
     *如果继承父类后，父类的构造方法有参数，那么需要在继承的时候传递参数--就是说在声明语句的时候就给父类后面加括号加参数
     *一般 参数不会固定写死，一般从子类传递过来
     */

        object Scala_Extends {

         def main(args: Array[String]): Unit = {
          val son: Son = new Son("zhangsan")
         }

        }
        //如果父类的构造有参数，那么在继承的时候要在父类后面加参数，调用顺序先父后子
        class Son(name:String)extends Father(name){
         println("111")
        }
        class Father(){
         println("222")
         def this(name:String ){
          this()
          println("444")
         }
         println("333")
        }


附赠：Java中对象创建过程，包括继承

    1、在方法区中检查要创建的对象所属的类的继承体系中的所有类的类模板是否加载。
    2、如果没有加载，由类加载器 (classloader) 按照从父到子的顺序依次加载所有类模板。
    3、如果类模板都加载过了，就不加载，保证类模板只有一份。
    4、依据所有类中的属性的定义信息在 GC 区中开辟空间，属性排序必须从父到子。
    5、JVM 把此空间全部写 0。
    6、执行父类属性的显式赋值。
    7、执行父类构造器。
    8、执行子类属性的显式赋值。
    9、执行子类构造器。
    10、返回对象首地址。

'''


# todo 1、单继承


# 父类
class Aniaml(object):
    type_name = '动物类'

    def __init__(self, name, gender, age):
        self.name = name
        self.age = age
        self.sex = gender

    def eat(self):
        print(f'打印父类的self:{self}')
        print('我是来自父类的eat()')


# 子类

class Person(Aniaml):
    pass


class Cat(Aniaml):
    def eat(self):
        print(f'我是子类中的的方法{self}我要吃香肠')


class Dog(Aniaml):
    pass


# 1.1 通过子类名可以直接调用父亲的属性和方法

print(Person.type_name)

Dog.eat('骨头')

# 通过子类对象也可以访问

ps = Person('yujiahao', 'male', 20)
ps.eat()
print(ps.__dict__)
ps.name = 'haoge'
print(ps.__dict__)

# 1.2 执行顺序
'''实例化对象时必须执行__init__方法,类中没有，从父类找，父类没有，从object类中找。先要执行自己类中的eat方法，自己类没有才能执行父类中的方法。'''

mao = Cat('咪咪', 'male', 2)
# 调用父类同名的方法
mao.eat()

# 1.3同时执行类以及父类方法
'''如果想执行父类的func方法，这个方法并且子类中使用那么有两个办法'''


# 方法一：父类.func(对象,其他参数)

class Fash(Aniaml):

    def __init__(self, name, gender, age, mind):
        # 方法1，可以通过父类名.func的方式调用
        Aniaml.__init__(self, name, gender, age)

        self.mind = mind


fash = Fash('兰寿', 'male', 2, '就知道吃')

print(fash.__dict__)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# 方法二:通过super关键字 super().func(参数)


class Dragon(Aniaml):
    def __init__(self, name, gender, age, job):
        super().__init__(name, gender, age)

        self.job = job

    def eat(self):
        super().eat()
        print(f'子类中龙的工作就是：{self.job}')


gragon = Dragon('我是龙', 'male', 2000, '下雨')
print(gragon.__dict__)
gragon.eat()


# 看个例子,可以手写结果在对比
class Base:
    def __init__(self, num):
        self.num = num

    def func1(self):
        print(self.num)
        self.func2()

    def func2(self):
        print(111, self.num)


class Foo(Base):
    def func2(self):
        print(222, self.num)


lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
    obj.func1()

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# todo 2、多继承

class ShenXian:  # 神仙
    def fei(self):
        print("神仙都会⻜")


class Monkey:  # 猴
    def chitao(self):
        print("猴⼦喜欢吃桃⼦")


class SunWukong(ShenXian, Monkey):  # 孙悟空是神仙, 同时也是⼀只猴
    pass


sxz = SunWukong()  # 孙悟空
sxz.chitao()  # 会吃桃⼦
sxz.fei()  # 会⻜

'''
此时, 孙悟空是⼀只猴⼦, 同时也是⼀个神仙. 那孙悟空继承了这两个类. 孙悟空⾃然就可以执⾏这两个类中的⽅法. 多继承⽤起来简单. 也很好理解. 
但是多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候. 这时该怎么办呢? 这时就涉及到如何查找⽗类⽅法的这么⼀个问题.
即MRO(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成MRO的.
'''

# 2.1经典类的多继承
'''
虽然在python3中已经不存在经典类了. 但是经典类的MRO最好还是学⼀学. 这是⼀种树形结构遍历的⼀个最直接的案例. 
在python的继承体系中. 我们可以把类与类继承关系化成⼀个树形结构的图. 来, 上代码:
'''


class A:
    pass


class B(A):
    pass


class C(A):
    pass


class D(B, C):
    pass


class E:
    pass


class F(D, E):
    pass


class G(F, D):
    pass


class H:
    pass


class Foo(H, G):
    pass


'''
这种mro画图就可以：
继承关系图已经有了. 如何进⾏查找就记住⼀个原则. 在经典类中采⽤的是深度优先，遍历⽅案. 
深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个.

类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C
                                                            +---+        
                                                 /------>   | A | <-----------\       
                                                 |          +---+             |
                                                 |                            |
                                                 |                            |
                                                 |                            |
                                                +---+                       +---+
                                                | B |                       | C |
                                                +---+                       +---+     
                                                 ^-------------\ /-------------^
                    +---+             +---+                   +---+     
                    | E |<------------| F |---------------->  | D |     
                    +---+             +---+                   +---+     
                                        ^
                                        |
        +---+                         +---+
        | H |                         | G |
        +---+                         +---+                       
          ^                             ^
          |                             |
          |                             |
          |                             |
          |             +-----+         |
          \-------------| Foo | --------/
                        +-----+
'''

# 2.2新式类的多继承


'''
mro序列

MRO是一个有序列表L，在类被创建时就计算出来。 通用计算公式为：

    mro(Child(Base1，Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
    （其中Child继承自Base1, Base2）
    
如果继承至一个基类：class B(A) 这时B的mro序列为

    mro( B ) = mro( B(A) )
    = [B] + merge( mro(A) + [A] )
    = [B] + merge( [A] + [A] )
    = [B,A]
如果继承至多个基类：class B(A1, A2, A3 …) 这时B的mro序列

    mro(B) = mro( B(A1, A2, A3 …) )
    = [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
    = ...
计算结果为列表，列表中至少有一个元素即类自己，如上述示例[A1,A2,A3]。merge操作是【C3算法】的核心。

【这种面试可能会手写】

'''

# 方法解析MRO，代码直观的看一下


'''
这个输出表示方法解析顺序如下：

    Composite 类
    ComponentB 类
    ComponentC 类
    ComponentA 类
    object 类
所以，当我们调用 comp.operation() 时，首先在 Composite 类中查找 operation 方法，如果没有找到，则继续在 ComponentB 类中查找，依此类推。
由于 ComponentB 类中定义了 operation 方法，因此调用的是 ComponentB 类的 operation 方法。

在这个例子中，Composite 类继承了 ComponentB 和 ComponentC，并且 ComponentB 的 operation 方法优先于 ComponentC 的 operation 方法被调用。
这是因为在 MRO 中，ComponentB 位于 ComponentC 之前。

'''


class ComponentA:
    def operation(self):
        print("来自 ComponentA 的操作")


class ComponentB(ComponentA):
    def operation(self):
        print("来自 ComponentB 的操作")


class ComponentC(ComponentA):
    def operation(self):
        print("来自 ComponentC 的操作")


class Composite(ComponentB, ComponentC):
    print('我是子类')


comp = Composite()
comp.operation()  # 输出: 来自 ComponentB 的操作

print(Composite.__mro__)

# C3线性算法，计算MRO


"""
如计算merge( [E,O], [C,E,F,O], [C] )
有三个列表 ：  ①        ②      ③


假设我们有三个列表：
    ① [E, O]
    ② [C, E, F, O]
    ③ [C]

我们需要合并这些列表来计算最终的 MRO。C3 线性化算法的步骤如下：

    1. 从每个列表的头部选择一个元素，选择的元素必须满足：它不能出现在任何其他列表的非头部位置。
    2. 将选择的元素添加到 MRO 列表中，并从所有列表中移除。
    3. 重复上述步骤，直到所有列表为空。

步骤：
    初始列表：
    ① [E, O]
    ② [C, E, F, O]
    ③ [C]

    第一步：
    头部元素是：E, C, C
    C 满足条件（它不在任何列表的非头部位置）
    选择 C：

    MRO: 
    [C]

    移除 C：

    ① [E, O]
    ② [E, F, O]
    ③ []

    第二步：
    头部元素是：E, E
    E 满足条件（它不在任何列表的非头部位置）
    选择 E：
    
    MRO: 
    [C, E]
    
    移除 E：
    
    ① [O]
    ② [F, O]
    ③ []
    
    第三步：
    头部元素是：O, F
    F 满足条件（它不在任何列表的非头部位置）
    选择 F：
    
    MRO: 
    [C, E, F]
    
    移除 F：
    
    ① [O]
    ② [O]
    ③ []
    
    第四步：
    头部元素是：O, O
    O 满足条件（它不在任何列表的非头部位置）
    选择 O：
    
    MRO: 
    [C, E, F, O]
    
    移除 O：
    
    ① []
    ② []
    ③ []
    
    最终结果：
    最终的 MRO 是：
    [C, E, F, O]
"""


# 看下面的例子，可以画出类继承图来看


class O:
    pass


class D(O):
    pass


class E(O):
    pass


class F(O):
    pass


class B(D, E):
    pass


class C(E, F):
    pass


class A(B, C):
    pass


'''

这是继承图：

     O
   / | \
  ^  ^  ^
 D   E   F
 |   |   |
 ^   ^   ^
 B   |   C
  \  |  /
    ^ ^
     A

计算mro(A)方式：

mro(A) = mro( A(B,C) )

原式= [A] + merge( mro(B),mro(C),[B,C] )

  mro(B) = mro( B(D,E) )
         = [B] + merge( mro(D), mro(E), [D,E] )  # 多继承
         = [B] + merge( [D,O] , [E,O] , [D,E] )  # 单继承mro(D(O))=[D,O]
         = [B,D] + merge( [O] , [E,O]  ,  [E] )  # 拿出并删除D
         = [B,D,E] + merge([O] ,  [O])
         = [B,D,E,O]

  mro(C) = mro( C(E,F) )
         = [C] + merge( mro(E), mro(F), [E,F] )
         = [C] + merge( [E,O] , [F,O] , [E,F] )
         = [C,E] + merge( [O] , [F,O]  ,  [F] )  # 跳过O，拿出并删除
         = [C,E,F] + merge([O] ,  [O])
         = [C,E,F,O]

原式= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C])
    = [A,B] + merge( [D,E,O], [C,E,F,O],   [C])
    = [A,B,D] + merge( [E,O], [C,E,F,O],   [C])  # 跳过E
    = [A,B,D,C] + merge([E,O],  [E,F,O])
    = [A,B,D,C,E] + merge([O],    [F,O])  # 跳过O
    = [A,B,D,C,E,F] + merge([O],    [O])
    = [A,B,D,C,E,F,O]
    
    
    那既然python提供了. 为什么我们还要如此⿇烦的计算MRO呢? 因为笔试.......你在笔试的时候, 是没有电脑的.
所以这个算法要知道. 并且简单的计算要会. 真是项⽬ 开发的时候很少有⼈这么去写代码.
这个说完了. 那C3到底怎么看更容易呢? 其实很简单. C3是把我们多个类产⽣的共同继 承留到最后去找.
所以. 我们也可以从图上来看到相关的规律. 这个要⼤家⾃⼰多写多画图就能感觉到了.
但是如果没有所谓的共同继承关系. 那⼏乎就当成是深度遍历就可以了
'''
